package com.test.practice.genericity.bounds;


/**
 * Genericity--Practice
 *
 * <p>
 * <? extends T> 是指   “上界通配符（Upper Bounds Wildcards）”
 * <? super T> 是指  “下界通配符（Lower Bounds Wildcards）”
 * <p>
 * <p>
 * 上界<? extends T>不能往里存，只能往外取
 * <p>
 * 下界<? super T>可以往里存，但往外取只能放在Object对象里
 *
 * java是单继承，所有继承的类构成一棵树。
 * 假设A和B都在一颗继承树里（否则super，extend这些词没意义）。
 *
 * A extend B 表示A是B的子类或者子孙，在B下面，所以叫有上界
 * A super B 表示A是B的父类或者祖先，在B的上面，所以叫有下界
 *
 * 由于树这个结构上下是不对称的，所以这两种表达区别很大。假设有两个泛型写在了函数定义里，作为函数形参（形参和实参有区别）：
 *
 * 1) 参数写成： T<? extends B>，由于指定了B为所有元素的“根”，你任何时候都可以安全的用B来使用容器里的元素，
 * 但是插入有问题，由于供奉B为祖先的子树有很多，不同子树并不兼容，由于实参可能来自于任何一颗子树，
 * 所以你的插入很可能破坏函数实参，所以，对这种写法的形参，禁止做插入操作，只做读取
 *
 * 2) 参数写成：T<? super B>，对于这个泛型，?代表容器里的元素类型，由于只规定了元素必须是B的超类，
 *  * 导致元素没有明确统一的“根”（除了Object这个必然的根），所以这个泛型你其实无法使用它，对吧，
 *  * 除了把元素强制转成Object。所以，对把参数写成这样形态的函数，你函数体内，只能对这个泛型做插入操作，而无法读
 *
 * ————————————————
 *
 *
 * @author: Jovi
 * @createTime: 2018-06-01 16:06
 **/
public class Genericity {
    public static void main(String[] args) {

        Plate<Fruit> p1 = new Plate(new Apple("apple1"));
        p1.set(new Apple("newappl"));
        System.out.println(p1.get().name);

        Plate<? extends Fruit> p2 = new Plate(new Apple("apple2"));
        //不能存入任何元素
        // p2.set(new Apple("newappl")); //error
        // p2.set(new Fruit("newappl")); //error
        // p2.set(new Food("newappl")); //error

        Fruit fruit = p2.get();
        Food food = p2.get();
        // Apple apple = p2.get(); //error 因为这里不确定放入盘子的是哪种水果，只能确定是水果，确定是食物

        System.out.println(fruit.name + food.name);

        Plate<? super Fruit> p3 = new Plate(new Apple("apple3"));
        p3.set(new Apple("apple"));
        p3.set(new Fruit("apple"));
        // p3.set(new Food("apple")); //error 这里只能存子类中的，不能存父类

        //因为取出来的东西不确定是什么
        Object object = p3.get();
        // Food object1 = p3.get(); //error
        // Fruit object2 = p3.get(); //error
        // Apple object3 = p3.get(); //error

        System.out.println(p3.get());
    }

}
